#include "device_resource_if.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "osal_io.h"
#include "osal_mem.h"
#include "osal_spinlock.h"
#include "watchdog_core.h"
#include "watchdog_if.h"

#define HDF_LOG_TAG stm32mp157_IWDG

#define BIT(n)  (0x1U<<(n))

/* IWDG registers */
#define IWDG_KR		0x00 /* Key register */
#define IWDG_PR		0x04 /* Prescaler Register */
#define IWDG_RLR	0x08 /* ReLoad Register */
#define IWDG_SR		0x0C /* Status Register */
#define IWDG_WINR	0x10 /* Windows Register */

/* IWDG_KR register bit mask */
#define KR_KEY_RELOAD	0xAAAA /* reload counter enable */
#define KR_KEY_ENABLE	0xCCCC /* peripheral enable */
#define KR_KEY_EWA	    0x5555 /* write access enable */
#define KR_KEY_DWA	    0x0000 /* write access disable */

/* IWDG_PR register */
#define PR_SHIFT	2
#define PR_MIN		BIT(PR_SHIFT)

// /* IWDG_RLR register values */
// #define RLR_MIN		0x2		/* min value recommended */
// #define RLR_MAX		GENMASK(11, 0)	/* max value of reload register */

/* IWDG_SR register bit mask */
#define SR_PVU	BIT(0) /* Watchdog prescaler value update */
#define SR_RVU	BIT(1) /* Watchdog counter reload value update */

/* set timeout to 100000 us */
#define TIMEOUT_US	100000
#define SLEEP_US	1000

struct Stm32Watchdog {
    struct WatchdogCntlr wdt;
    volatile unsigned char *regBase;
    uint32_t phyBase;
    uint32_t regStep;
    OsalSpinlock lock;
};

// static inline uint32_t reg_read(void *base, uint32_t reg)
// {
// 	return readl((uint32_t)base + reg);
// }

static inline void reg_write(void *base, uint32_t reg, uint32_t val)
{
	writel(val, (uint32_t)base + reg);
}

static int32_t Stm32WatchdogStart(struct WatchdogCntlr *wdt)
{
    struct Stm32Watchdog *hwdt = NULL;

    if (wdt == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    hwdt = (struct Stm32Watchdog *)wdt;
    
    // start 
    
    return HDF_SUCCESS;
}

#define HIWDT_CLOCK_HZ (3 * 1000 * 1000)
static int32_t Stm32WatchdogSetTimeout(struct WatchdogCntlr *wdt, uint32_t seconds)
{
    unsigned int value;
    unsigned int maxCnt = ~0x00;
    unsigned int maxSeconds = maxCnt / HIWDT_CLOCK_HZ;
    struct Stm32Watchdog *hwdt = NULL;

    if (seconds == 0 || seconds > maxSeconds) {
        value = maxCnt;
    } else {
        value = seconds * HIWDT_CLOCK_HZ;
    }

    if (wdt == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    hwdt = (struct Stm32Watchdog *)wdt;

    // set time out

    return HDF_SUCCESS;
}

static int32_t Stm32WatchdogGetTimeout(struct WatchdogCntlr *wdt, uint32_t *seconds)
{
    // unsigned int value;
    struct Stm32Watchdog *hwdt = NULL;

    if (wdt == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    hwdt = (struct Stm32Watchdog *)wdt;

    // get timeout

    
    return HDF_SUCCESS;
}


static int32_t Stm32WatchdogFeed(struct WatchdogCntlr *wdt)
{
    struct Stm32Watchdog *hwdt = NULL;

    if (wdt == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    hwdt = (struct Stm32Watchdog *)wdt;

    /* reload watchdog */
	reg_write((void *)hwdt->regBase, IWDG_KR, KR_KEY_RELOAD);
    return HDF_SUCCESS;
}

static struct WatchdogMethod g_method = {
    // .getStatus = Stm32WatchdogGetStatus,
    .start = Stm32WatchdogStart,
    // .stop = Stm32WatchdogStop,
    .setTimeout = Stm32WatchdogSetTimeout,
    .getTimeout = Stm32WatchdogGetTimeout,
    .feed = Stm32WatchdogFeed,
};

static int32_t Stm32WatchdogReadDrs(struct Stm32Watchdog *hwdt, const struct DeviceResourceNode *node)
{
    int32_t ret;
    struct DeviceResourceIface *drsOps = NULL;

    drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    if (drsOps == NULL || drsOps->GetUint32 == NULL) {
        HDF_LOGE("%s: invalid drs ops!", __func__);
        return HDF_FAILURE;
    }

    ret = drsOps->GetUint32(node, "regBase", &hwdt->phyBase, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read regBase fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint32(node, "regStep", &hwdt->regStep, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read regStep fail!", __func__);
        return ret;
    }

    return HDF_SUCCESS;
}

static int32_t Stm32WatchdogBind(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct Stm32Watchdog *hwdt = NULL;

    HDF_LOGI("%s: Enter", __func__);
    if (device == NULL || device->property == NULL) {
        HDF_LOGE("%s: device or property is null!", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }

    hwdt = (struct Stm32Watchdog *)OsalMemCalloc(sizeof(*hwdt));
    if (hwdt == NULL) {
        HDF_LOGE("%s: malloc hwdt fail!", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = Stm32WatchdogReadDrs(hwdt, device->property);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read drs fail:%d", __func__, ret);
        OsalMemFree(hwdt);
        return ret;
    }

    hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep);
    if (hwdt->regBase == NULL) {
        HDF_LOGE("%s: ioremap regbase fail!", __func__);
        OsalMemFree(hwdt);
        return HDF_ERR_IO;
    }

    hwdt->wdt.priv = (void *)device->property;
    hwdt->wdt.ops = &g_method;
    hwdt->wdt.device = device;
    ret = WatchdogCntlrAdd(&hwdt->wdt);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: err add watchdog:%d", __func__, ret);
        OsalIoUnmap((void *)hwdt->regBase);
        OsalMemFree(hwdt);
        return ret;
    }
    HDF_LOGI("%s: dev service %s init success!", __func__, HdfDeviceGetServiceName(device));
    return HDF_SUCCESS;
}

static int32_t Stm32WatchdogInit(struct HdfDeviceObject *device)
{
    (void)device;
    return HDF_SUCCESS;
}

static void Stm32WatchdogRelease(struct HdfDeviceObject *device)
{
    struct WatchdogCntlr *wdt = NULL;
    struct Stm32Watchdog *hwdt = NULL;

    HDF_LOGI("%s: enter", __func__);
    if (device == NULL) {
        return;
    }

    wdt = WatchdogCntlrFromDevice(device);
    if (wdt == NULL) {
        return;
    }
    WatchdogCntlrRemove(wdt);

    hwdt = (struct Stm32Watchdog *)wdt;
    if (hwdt->regBase != NULL) {
        OsalIoUnmap((void *)hwdt->regBase);
        hwdt->regBase = NULL;
    }
    OsalMemFree(hwdt);
}

struct HdfDriverEntry g_watchdogDriverEntry = {
    .moduleVersion = 1,
    .Bind = Stm32WatchdogBind,
    .Init = Stm32WatchdogInit,
    .Release = Stm32WatchdogRelease,
    .moduleName = "HDF_PLATFORM_WATCHDOG",
};
HDF_INIT(g_watchdogDriverEntry);
